// 版权所有20092010 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// FreeBSD系统调用。
// 该文件编译为普通Go代码，
// 但它也是mksyscall的输入，
// 解析
// 请注意，有时我们使用小写
// syscall_bsd.go或syscall_unix.go中。

package syscall

import (
	"sync"
	"unsafe"
)

const (
	_SYS_FSTAT_FREEBSD12         = 551 // /{int-fstat（int-fd，Out-struct-stat*sb）}（_-In-z-char*path，_-Out-struct statfs*buf）}
	_SYS_FSTATFS_FREEBSD12       = 556 // /{int-fstatfs（int-fd，_-Out-struct statfs*buf）}
	_SYS_GETFSSTAT_FREEBSD12     = 557 // /{int-getfsstat（_-Out-Out-opt_fsize）struct statfs*buf，long-bufsize，int模式）}
)

// 参见https:
var (
	osreldateOnce sync.Once
	osreldate     uint32
)

// INO64_FIRST from/usr/src/lib/libc/sys/compat-INO64.h 
const _ino64First = 1200031

func supportsABI(ver uint32) bool {
	osreldateOnce.Do(func() { osreldate, _ = SysctlUint32("kern.osreldate") })
	return osreldate >= ver
}

type SockaddrDatalink struct {
	Len    uint8
	Family uint8
	Index  uint16
	Type   uint8
	Nlen   uint8
	Alen   uint8
	Slen   uint8
	Data   [46]int8
	raw    RawSockaddrDatalink
}

// 将“kern.hostname”翻译为[].\u C_int{0,1,2,3}。
func nametomib(name string) (mib []_C_int, err error) {
	const siz = unsafe.Sizeof(mib[0])

	// 注意（rsc）：将缓冲区设置为有
	// size CTL_MAXNAME+2，但仅使用CTL_MAXNAME 
	// 作为大小似乎很奇怪。我不知道为什么这里有+2，但是
	// 内核使用+2来实现此函数。
	// 我担心如果我们不在这里包含+2，内核会出现一个错误BCDEFG
	// 我们将得到内存损坏。
	var buf [CTL_MAXNAME + 2]_C_int
	n := uintptr(CTL_MAXNAME) * siz

	p := (*byte)(unsafe.Pointer(&buf[0]))
	bytes, err := ByteSliceFromString(name)
	if err != nil {
		return nil, err
	}

	// Magic sysctl:“设置”0.3为字符串名
	// 允许您读回整数数组形式。
	if err = sysctl([]_C_int{0, 3}, p, &n, &bytes[0], uintptr(len(name))); err != nil {
		return nil, err
	}
	return buf[0 : n/siz], nil
}

func direntIno(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
}

func direntReclen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}

func direntNamlen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}

func Pipe(p []int) error {
	return Pipe2(p, 0)
}

// sysnb pipe2（p*[2]\u C_int，flags int）（错误）

func Pipe2(p []int, flags int) error {
	if len(p) != 2 {
		return EINVAL
	}
	var pp [2]_C_int
	err := pipe2(&pp, flags)
	p[0] = int(pp[0])
	p[1] = int(pp[1])
	return err
}

func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, error) {
	var value IPMreqn
	vallen := _Socklen(SizeofIPMreqn)
	errno := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen)
	return &value, errno
}

func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) {
	return setsockopt(fd, level, opt, unsafe.Pointer(mreq), unsafe.Sizeof(*mreq))
}

func Accept4(fd, flags int) (nfd int, sa Sockaddr, err error) {
	var rsa RawSockaddrAny
	var len _Socklen = SizeofSockaddrAny
	nfd, err = accept4(fd, &rsa, &len, flags)
	if err != nil {
		return
	}
	if len > SizeofSockaddrAny {
		panic("RawSockaddrAny too small")
	}
	sa, err = anyToSockaddr(&rsa)
	if err != nil {
		Close(nfd)
		nfd = 0
	}
	return
}

func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
	var (
		_p0          unsafe.Pointer
		bufsize      uintptr
		oldBuf       []statfs_freebsd11_t
		needsConvert bool
	)

	if len(buf) > 0 {
		if supportsABI(_ino64First) {
			_p0 = unsafe.Pointer(&buf[0])
			bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
		} else {
			n := len(buf)
			oldBuf = make([]statfs_freebsd11_t, n)
			_p0 = unsafe.Pointer(&oldBuf[0])
			bufsize = unsafe.Sizeof(statfs_freebsd11_t{}) * uintptr(n)
			needsConvert = true
		}
	}
	var sysno uintptr = SYS_GETFSSTAT
	if supportsABI(_ino64First) {
		sysno = _SYS_GETFSSTAT_FREEBSD12
	}
	r0, _, e1 := Syscall(sysno, uintptr(_p0), bufsize, uintptr(flags))
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	if e1 == 0 && needsConvert {
		for i := range oldBuf {
			buf[i].convertFrom(&oldBuf[i])
		}
	}
	return
}

func setattrlistTimes(path string, times []Timespec) error {
	// 用于Darwin上的UtimesNano 
	return ENOSYS
}

func Stat(path string, st *Stat_t) (err error) {
	var oldStat stat_freebsd11_t
	if supportsABI(_ino64First) {
		return fstatat_freebsd12(_AT_FDCWD, path, st, 0)
	}
	err = stat(path, &oldStat)
	if err != nil {
		return err
	}

	st.convertFrom(&oldStat)
	return nil
}

func Lstat(path string, st *Stat_t) (err error) {
	var oldStat stat_freebsd11_t
	if supportsABI(_ino64First) {
		return fstatat_freebsd12(_AT_FDCWD, path, st, _AT_SYMLINK_NOFOLLOW)
	}
	err = lstat(path, &oldStat)
	if err != nil {
		return err
	}

	st.convertFrom(&oldStat)
	return nil
}

func Fstat(fd int, st *Stat_t) (err error) {
	var oldStat stat_freebsd11_t
	if supportsABI(_ino64First) {
		return fstat_freebsd12(fd, st)
	}
	err = fstat(fd, &oldStat)
	if err != nil {
		return err
	}

	st.convertFrom(&oldStat)
	return nil
}

func Fstatat(fd int, path string, st *Stat_t, flags int) (err error) {
	var oldStat stat_freebsd11_t
	if supportsABI(_ino64First) {
		return fstatat_freebsd12(fd, path, st, flags)
	}
	err = fstatat(fd, path, &oldStat, flags)
	if err != nil {
		return err
	}

	st.convertFrom(&oldStat)
	return nil
}

func Statfs(path string, st *Statfs_t) (err error) {
	var oldStatfs statfs_freebsd11_t
	if supportsABI(_ino64First) {
		return statfs_freebsd12(path, st)
	}
	err = statfs(path, &oldStatfs)
	if err != nil {
		return err
	}

	st.convertFrom(&oldStatfs)
	return nil
}

func Fstatfs(fd int, st *Statfs_t) (err error) {
	var oldStatfs statfs_freebsd11_t
	if supportsABI(_ino64First) {
		return fstatfs_freebsd12(fd, st)
	}
	err = fstatfs(fd, &oldStatfs)
	if err != nil {
		return err
	}

	st.convertFrom(&oldStatfs)
	return nil
}

func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
	if supportsABI(_ino64First) {
		if basep == nil || unsafe.Sizeof(*basep) == 8 {
			return getdirentries_freebsd12(fd, buf, (*uint64)(unsafe.Pointer(basep)))
		}
		// freebsd12系统调用需要64位的基址。在32位机器上
		// 我们不能只使用传入的基址。请参见#32498.
		var base uint64 = uint64(*basep)
		n, err = getdirentries_freebsd12(fd, buf, &base)
		*basep = uintptr(base)
		if base>>32 != 0 {
			// 我们不能将基址填充回uintptr，因此任何
			// 将来的调用都会受到怀疑。生成一个错误。
			// getdirentries允许EIO。
			err = EIO
		}
		return
	}

	// 旧的系统调用项比新的小。使用原始
	// 缓冲区大小的1/4四舍五入到DIRBLKSIZ（请参阅/usr/src/libc/libc/sys/getdirentries.c）.
	oldBufLen := roundup(len(buf)/4, _dirblksiz)
	oldBuf := make([]byte, oldBufLen)
	n, err = getdirentries(fd, oldBuf, basep)
	if err == nil && n > 0 {
		n = convertFromDirents11(buf, oldBuf[:n])
	}
	return
}

func Mknod(path string, mode uint32, dev uint64) (err error) {
	var oldDev int
	if supportsABI(_ino64First) {
		return mknodat_freebsd12(_AT_FDCWD, path, mode, dev)
	}
	oldDev = int(dev)
	return mknod(path, mode, oldDev)
}

// 将x四舍五入到y的最接近倍数，大于或等于x。
// 
// 从/usr/include/sys/param.h宏进行计数和四舍五入。
// /#定义四舍五入（x，y）（（（x）+（y）-1））/（y））*（y））
func roundup(x, y int) int {
	return ((x + y - 1) / y) * y
}

func (s *Stat_t) convertFrom(old *stat_freebsd11_t) {
	*s = Stat_t{
		Dev:           uint64(old.Dev),
		Ino:           uint64(old.Ino),
		Nlink:         uint64(old.Nlink),
		Mode:          old.Mode,
		Uid:           old.Uid,
		Gid:           old.Gid,
		Rdev:          uint64(old.Rdev),
		Atimespec:     old.Atimespec,
		Mtimespec:     old.Mtimespec,
		Ctimespec:     old.Ctimespec,
		Birthtimespec: old.Birthtimespec,
		Size:          old.Size,
		Blocks:        old.Blocks,
		Blksize:       old.Blksize,
		Flags:         old.Flags,
		Gen:           uint64(old.Gen),
	}
}

func (s *Statfs_t) convertFrom(old *statfs_freebsd11_t) {
	*s = Statfs_t{
		Version:     _statfsVersion,
		Type:        old.Type,
		Flags:       old.Flags,
		Bsize:       old.Bsize,
		Iosize:      old.Iosize,
		Blocks:      old.Blocks,
		Bfree:       old.Bfree,
		Bavail:      old.Bavail,
		Files:       old.Files,
		Ffree:       old.Ffree,
		Syncwrites:  old.Syncwrites,
		Asyncwrites: old.Asyncwrites,
		Syncreads:   old.Syncreads,
		Asyncreads:  old.Asyncreads,
		// Spare 
		Namemax: old.Namemax,
		Owner:   old.Owner,
		Fsid:    old.Fsid,
		// Charspare 
		// Fstypename 
		// Mntfromname 
		// MNTONAME 
	}

	sl := old.Fstypename[:]
	n := clen(*(*[]byte)(unsafe.Pointer(&sl)))
	copy(s.Fstypename[:], old.Fstypename[:n])

	sl = old.Mntfromname[:]
	n = clen(*(*[]byte)(unsafe.Pointer(&sl)))
	copy(s.Mntfromname[:], old.Mntfromname[:n])

	sl = old.Mntonname[:]
	n = clen(*(*[]byte)(unsafe.Pointer(&sl)))
	copy(s.Mntonname[:], old.Mntonname[:n])
}

func convertFromDirents11(buf []byte, old []byte) int {
	const (
		fixedSize    = int(unsafe.Offsetof(Dirent{}.Name))
		oldFixedSize = int(unsafe.Offsetof(dirent_freebsd11{}.Name))
	)

	dstPos := 0
	srcPos := 0
	for dstPos+fixedSize < len(buf) && srcPos+oldFixedSize < len(old) {
		var dstDirent Dirent
		var srcDirent dirent_freebsd11

		// 如果写入多个目录项，有时当我们到达最后一个目录项时，
		// 我们的旧上限可能小于目录的大小。
		copy((*[unsafe.Sizeof(srcDirent)]byte)(unsafe.Pointer(&srcDirent))[:], old[srcPos:])

		reclen := roundup(fixedSize+int(srcDirent.Namlen)+1, 8)
		if dstPos+reclen > len(buf) {
			break
		}

		dstDirent.Fileno = uint64(srcDirent.Fileno)
		dstDirent.Off = 0
		dstDirent.Reclen = uint16(reclen)
		dstDirent.Type = srcDirent.Type
		dstDirent.Pad0 = 0
		dstDirent.Namlen = uint16(srcDirent.Namlen)
		dstDirent.Pad1 = 0

		copy(dstDirent.Name[:], srcDirent.Name[:srcDirent.Namlen])
		copy(buf[dstPos:], (*[unsafe.Sizeof(dstDirent)]byte)(unsafe.Pointer(&dstDirent))[:])
		padding := buf[dstPos+fixedSize+int(dstDirent.Namlen) : dstPos+reclen]
		for i := range padding {
			padding[i] = 0
		}

		dstPos += int(dstDirent.Reclen)
		srcPos += int(srcDirent.Reclen)
	}

	return dstPos
}

/*
 * Exposed directly
 */
// sys Access（路径字符串，模式uint32）（错误）
// sys Adjtime（delta*Timeval，olddta*Timeval）（错误）
// sys Chdir（路径字符串）（错误）
// sys Chflags（路径字符串，标志int）（错误）
// sys Chmod（路径字符串，模式uint32）（错误）
// sys CHOOT（路径字符串，uid int，gid int）（错误）
// sys Chroot（路径字符串）（错误）
// sys Close（fd int）（错误）
// sys Dup（fd int）（nfd int，错误）
// sys Fchflags（fd int，flags int）（错误）
// sys Fchmod（fd int，mode uint32）（错误）
// sys Fchown（fd int int，uid int，gid int）（错误）
// sys AFTCHOW（fd int int，name int）（val int int，err）
// sys fstat（fd int，stat*stat freebsd11_t）（错误）
// sys fstat_freebsd12（fd int，stat*stat_t）（错误）=sys fstat freebsd12 
// sys fstat（fd int，路径字符串，stat*stat freebsd11_t，flags int）（错误）
// SYS fstatfs（fd int，stat*statfs\u freebsd11\u t）（error error）
// sys-Fsync（fd-int）（error error）
// sys-Ftruncate（fd-int，length int64）（error error）
// sys-getdirentries（fd-int，buf[]byte，basep*uintpr）（n-int，error）
// SYS Getdtablesize（）（size int）
// sysnb Getegid（）（egid int）
// sysnb Geteuid（）（uid int）
// sysnb Getgid（）（gid int）
// sysnb Getpgid（pid int）（pgid int，error error）
// sysnb Getpid（）（pid int）
// sysnb Getppid（）（ppid int）
// sys Getpriority（who int，who int）（prio int，error error）
// sysnb Getrlimit（who int，lim*Rlimit）（error error）
// sysnb（pid int）（sid int，error error）
// sysnb Gettimeofday（tv*Timeval）（error error）
// sysnb Getuid（）（uid int）
// sys Issetugid（）（污染bool）
// sys Kill（pid int，signum信号）（error）
// sys Kqueue（）（fd int，error）
// sys Link（路径字符串，链接字符串）（错误）
// sys Listen（s int，backlog int）（错误）
// sys lstat（路径字符串，stat*stat freebsd11_t）（错误）
// sys mknod（路径字符串，模式uint32，开发int）（错误）
// sys mknodat_freebsd12（fd int，路径字符串，模式uint32，开发uint64）（错误）=sys mknodat_freebsd12 
// sys Pathconf（路径字符串，名称int）（val int，error error）
// sys Pread（fd int，p[]byte，offset int64）（n int，error error）
// sys Pwrite（fd int，p[]byte，offset int64）（n int，error）
// sys Readlink（路径字符串，buf[]字节）（n int，error error）
// sys Rename（从字符串到字符串）（errr error）
// sys Revoke（路径字符串）（error error error）
// sys Seek（fd int，offset int64，whence int）（newoffset int64，err error）=SYS_LSEEK 
// SYS Select（n int，r*FdSet，w*FdSet，e*FdSet，Timeval）（error error）
// sysnb Setegid（egid int）（error error）
// sysnb Seteuid（euid int）（error）
// sysnb Setgid（gid int）（error）
// sys statfs\U freebsd12（路径字符串，stat*statfs\U t）（错误）=sys statfs\U freebsd12 
// sys Symlink（路径字符串，链接字符串）（错误）
// sys Sync（）（错误）
// sys Truncate（路径字符串，长度int64）（错误）
// sys Umask（新掩码int）（旧掩码int）
// sys Undelete（路径字符串）（错误）
// sys Unlink（路径字符串）（错误）
// sys Unmount（路径字符串，标志int）（错误）
// sys write（fd int，p[]字节）（n int，错误）
// sys munmap（地址uintptr，长度uintptr）（err error）
// sys readlen（fd int，buf*字节，nbuf int）（n int，err err error）=sys READ 
// sys writelen（fd int，buf*字节，nbuf int int err）（n int，err err err error）=sys WRITE 
// sys accept4（fd int，rsa*RawSockaddrAny，addrlen*_Socklen，flags int）（nfd int，error error）
// sys utimensat（dirfd int，path string，times*[2]Timespec，flag int）（error error）
